﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.Serialization;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using Folks.UI.Adapters;

namespace Folks.UI
{
	[NonVisualControl]
	[DefaultProperty("Enabled")]
	[Designer(typeof(Folks.UI.Design.FolksManagerDesigner))]
	[ToolboxData("<{0}:FolksManager runat=server></{0}:FolksManager>")]
	public class FolksManager : WebControl, ISerializable
	{
		#region Fields

		private static string __EVENTVALIDATION = "__EVENTVALIDATION";
        //private static string __EVENTTARGET = "__EVENTTARGET";
        //private static string __EVENTARGUMENT = "__EVENTARGUMENT";
		private static string __VIEWSTATE = "__VIEWSTATE";
		private static string __FOLKSCONTAINER = "__FOLKSCONTAINER";
		private static string __LASTFOCUS = "__LASTFOCUS";
		
		#endregion

		#region Constructors

		public FolksManager() { }
		public FolksManager(SerializationInfo info, StreamingContext context) { }
		
		#endregion

		#region Public Properties

		[Browsable(false)]
		public bool IsFolksPostBack
		{
			get
			{
				return "XMLHttpRequest".Equals(Page.Request.Headers["x-requested-with"]) && Page.IsPostBack;
			}
		}

		[Browsable(false)]
		public bool IsFromContainer
		{
			get
			{
				return !string.IsNullOrEmpty(Page.Request[__FOLKSCONTAINER]);
			}
		}

        [Browsable(false)]
        private string CurrentContainerID
        {
            get
            {
                if (IsFromContainer)
                    return Page.Request[__FOLKSCONTAINER];
                return string.Empty;
            }
        }

		[Browsable(false)]
		public Control CurrentContainer
		{
			get
			{
                if (IsFromContainer)
                    return FindControl(Page, CurrentContainerID);
                return null;
			}
		}

        [Category("Behavior")]
		[DefaultValue(false)]
		[Description("Capture all postbacks with Folks.")]
		public bool CaptureAll
		{
			get
			{
				if (ViewState["CaptureAll"] == null)
					return false;
				return (bool)ViewState["CaptureAll"];
			}
			set { ViewState["CaptureAll"] = value; }
		}

		[Category("Behavior")]
		[DefaultValue(false)]
		[Description(@"Hiding ViewState can turn the application more fast. 
		To hide it, you need to add the Folks.UI.Adapters.FolksPageAdapter to your .browser file.
		The SessionState provider under database can turn it more slow.")]
		public bool OptimizeViewState
		{
			get
			{
				if (ViewState["OptimizeViewState"] == null)
					return false;
				return (bool)ViewState["OptimizeViewState"];
			}
			set { ViewState["OptimizeViewState"] = value; }
		}
		#endregion

		#region Overriden Methods

		protected override void OnInit(EventArgs e)
		{
			EnsureRequirements();
			Initialize();
			base.OnInit(e);
		}

		protected override void Render(HtmlTextWriter writer)
		{
			writer.WriteLine("<script type=\"text/javascript\">FolksManager.getInstance().captureAll(" + CaptureAll.ToString().ToLower() + ");</script>");
		}

        #endregion

		#region Public Methods
		public static FolksManager GetCurrent(Page page)
		{
			return (FolksManager) FindControl(page, delegate(Control c) { return typeof(FolksManager).IsInstanceOfType(c); });
		}

		internal static void EnsureFolksManager(Page page)
		{
			if (FolksManager.GetCurrent(page) == null)
				throw new Exception(string.Format("The page {0} must contain a {1} control.", page.Title, typeof(FolksManager).Name));
		}
		#endregion

		#region Private Methods

		private void Initialize()
		{
			RegisterClientScriptInclude(Page.ClientScript.GetWebResourceUrl(this.GetType(), "Folks.UI.Js.Jquery.js"));
			RegisterClientScriptInclude(Page.ClientScript.GetWebResourceUrl(this.GetType(), "Folks.UI.Js.Folks.js"));
			if (IsFolksPostBack)
				Page.SetRenderMethodDelegate(new RenderMethod(RenderResponse));
			
		}

		private void RegisterClientScriptInclude(string src)
		{
			HtmlGenericControl script = new HtmlGenericControl("script");
			script.Attributes.Add("type", "text/javascript");
			script.Attributes.Add("src", src);
			if (!Page.Header.Controls.Contains(script))
				Page.Header.Controls.Add(script);
		}

		private void EnsureRequirements()
		{
			if (OptimizeViewState && !(Page.PageAdapter is FolksPageAdapter))
				throw new Exception(string.Format("Configure {0} as the PageAdapter to enable {1} property.", typeof(FolksPageAdapter).Name, "OptimizeViewState"));
		}

		private void RenderResponse(HtmlTextWriter writer, Control control)
		{
			using (FolksParserWriter fw = new FolksParserWriter())
			{
				using (HtmlTextWriter htw = new HtmlTextWriter(fw))
				{
					fw.ContainerToWatch = (IsFromContainer) ? CurrentContainer.ClientID : string.Empty;
					Page.Form.RenderControl(htw);
					string response = (IsFromContainer) ? CreateContainerResponse(fw) : fw.ToString();
					writer.Write(response);
				}
			}
		}

		private string CreateContainerResponse(FolksParserWriter fw)
		{
			string viewState = (fw.HiddenFields.ContainsKey(__VIEWSTATE)) ? fw.HiddenFields[__VIEWSTATE] : string.Empty;
			string validation = (fw.HiddenFields.ContainsKey(__EVENTVALIDATION)) ? fw.HiddenFields[__EVENTVALIDATION] : string.Empty;
			string lastFocus = (fw.HiddenFields.ContainsKey(__LASTFOCUS)) ? fw.HiddenFields[__LASTFOCUS] : string.Empty;
			return CurrentContainer.ClientID + FolksContainer.ResponseSeparator +
					viewState + FolksContainer.ResponseSeparator +
					validation + FolksContainer.ResponseSeparator +
					lastFocus + FolksContainer.ResponseSeparator +
					fw.ContainerResponse;
		}

		private bool IsContainerChild(Control control)
		{
			if (IsFromContainer)
				return GetControls(CurrentContainer).Contains(control);
			return false;
		}

        internal Control FindControl(Control control, string clientID)
        {
            if (control.ClientID == clientID)
                return control;
            if (control.HasControls())
            {
                foreach (Control ctrl in control.Controls)
                {
                    Control found = FindControl(ctrl, clientID);
                    if (found != null)
                        return found;
                }
            }
            return null;
        }

        internal static Control FindControl(Control control, Predicate<Control> match)
        {
            if (match(control))
                return control;
            if (control.HasControls())
            {
                foreach (Control ctrl in control.Controls)
                {
                    Control found = FindControl(ctrl, match);
                    if (found != null)
                        return found;
                }
            }
            return null;
        }

        private static List<Control> GetControls(Control root)
        {
            List<Control> controls = new List<Control>();
            controls.Add(root);
            if (root.HasControls())
                foreach (Control control in root.Controls)
                    controls.AddRange(GetControls(control));
            return controls;
        }
		#endregion

		#region ISerializable Members

		public virtual void GetObjectData(SerializationInfo info, StreamingContext context)	{ }

		#endregion
	}
}
